package com.breeze.blockchain.common.exception;

import static com.breeze.blockchain.common.exception.BizErrorCode.USER_STATUS_LOCK_ERROR;

import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.breeze.components.core.exception.ApiException;
import com.breeze.components.core.exception.error.ErrorCode;
import com.breeze.components.web.core.ResponseDTO;
import jakarta.servlet.http.HttpServletRequest;
import java.sql.BatchUpdateException;
import java.sql.SQLException;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.UncheckedExecutionException;
import org.apache.ibatis.exceptions.PersistenceException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

/**
 * 全局异常处理器
 *
 * @author breeze
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionInterceptor {

  @ExceptionHandler(NotLoginException.class)
  public ResponseDTO<?> handlerNotLoginException(NotLoginException nle) {
    String message;
    if (nle.getType().equals(NotLoginException.NOT_TOKEN)
        || nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
      message = "请先登录";
    } else if (nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
      message = "凭证已过期";
    } else if (nle.getType().equals(NotLoginException.BE_REPLACED)) {
      message = "您的账号在其他地方登陆,请重新登陆";
    } else if (nle.getType().equals(NotLoginException.KICK_OUT)) {
      message = "您的账号被踢下线";
    } else if (nle.getType().equals(NotLoginException.TOKEN_FREEZE)) {
      message = "您的账号已被冻结";
    } else if (nle.getType().equals(NotLoginException.NO_PREFIX)) {
      message = "凭证异常,请联系管理员";
    } else {
      message = "请先登录";
    }
    return ResponseDTO.fail(new ApiException(ErrorCode.PLACEHOLDER, message));
  }

  @ExceptionHandler({NotPermissionException.class, NotRoleException.class})
  public ResponseDTO<?> handlerException() {
    return ResponseDTO.fail(new ApiException(ErrorCode.NO_PERMISSION_ERROR));
  }

  @ExceptionHandler(DisableServiceException.class)
  public ResponseDTO<?> handlerDisableServiceException() {
    return ResponseDTO.fail(new ApiException(USER_STATUS_LOCK_ERROR));
  }

  @ExceptionHandler(value = IllegalArgumentException.class)
  public ResponseDTO<?> illegalArgExHandler(
      IllegalArgumentException e, HttpServletRequest request) {
    log.warn("请求地址'{}',参数异常'{}'", request.getRequestURI(), e.getMessage(), e);
    return ResponseDTO.fail(new ApiException(ErrorCode.PARAM_ERROR_ARGS, e.getMessage()));
  }

  @ExceptionHandler(NoHandlerFoundException.class)
  public ResponseDTO<?> noHandlerFoundException(
      NoHandlerFoundException e, HttpServletRequest request) {
    log.error("请求地址'{}',权限校验失败'{}'", request.getRequestURI(), e.getMessage(), e);
    return ResponseDTO.fail(new ApiException(ErrorCode.Client.REQUEST_NOT_FOUND));
  }

  @ExceptionHandler(DuplicateKeyException.class)
  public ResponseDTO<?> sql(DuplicateKeyException e, HttpServletRequest request) {
    log.warn("请求地址'{}',失败'{}'", request.getRequestURI(), e.getMessage());
    return ResponseDTO.fail(new ApiException(ErrorCode.Business.DUPLICATE_KEY_EXCEPTION));
  }

  @ExceptionHandler(SQLException.class)
  public ResponseDTO<?> sql(SQLException e, HttpServletRequest request) {
    log.error("请求地址'{}',权限校验失败'{}'", request.getRequestURI(), e.getMessage(), e);
    return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR));
  }

  @ExceptionHandler(BatchUpdateException.class)
  public ResponseDTO<?> sql(BatchUpdateException e, HttpServletRequest request) {
    log.error("请求地址'{}',权限校验失败'{}'", request.getRequestURI(), e.getMessage(), e);
    return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR));
  }

  @ExceptionHandler(PersistenceException.class)
  public ResponseDTO<?> sql(PersistenceException e, HttpServletRequest request) {
    log.error("请求地址'{}',batis 异常'{}'", request.getRequestURI(), e.getMessage(), e);
    return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR));
  }

  /** 请求方式不支持 */
  @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
  public ResponseDTO<?> handleHttpRequestMethodNotSupported(
      HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
    log.error("请求地址'{}',不支持'{}'请求", request.getRequestURI(), e.getMethod());
    return ResponseDTO.fail(
        new ApiException(ErrorCode.Client.COMMON_REQUEST_METHOD_INVALID, e.getMethod()));
  }

  /** 业务异常 */
  @ExceptionHandler(ApiException.class)
  public ResponseDTO<?> handleServiceException(ApiException e) {
    log.info(e.getMessage(), e);
    return ResponseDTO.fail(e, e.getPayload());
  }

  /** 捕获缓存类当中的错误 */
  @ExceptionHandler(UncheckedExecutionException.class)
  public ResponseDTO<?> handleServiceException(UncheckedExecutionException e) {
    log.error(e.getMessage(), e);
    return ResponseDTO.fail(new ApiException(ErrorCode.Internal.GET_CACHE_FAILED, e.getMessage()));
  }

  /** 拦截未知的运行时异常 */
  @ExceptionHandler(RuntimeException.class)
  public ResponseDTO<?> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
    String errorMsg = String.format("请求地址'%s',发生未知异常.", request.getRequestURI());
    log.error(errorMsg, e);
    return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, e.getMessage()));
  }

  /** 系统异常 */
  @ExceptionHandler(Exception.class)
  public ResponseDTO<?> handleException(Exception e, HttpServletRequest request) {
    String errorMsg = String.format("请求地址'%s',发生未知异常.", request.getRequestURI());
    log.error(errorMsg, e);
    return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, e.getMessage()));
  }

  /** 自定义验证异常 */
  @ExceptionHandler(BindException.class)
  public ResponseDTO<?> handleBindException(BindException e) {
    log.error(e.getMessage(), e);
    String message = e.getAllErrors().getFirst().getDefaultMessage();
    return ResponseDTO.fail(
        new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, message));
  }

  /** 自定义验证异常 */
  @ExceptionHandler(MethodArgumentNotValidException.class)
  public ResponseDTO<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
    log.warn(e.getMessage(), e);
    if (e.getBindingResult().getFieldError() != null) {
      return ResponseDTO.fail(
          new ApiException(
              ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID,
              e.getBindingResult().getFieldError().getDefaultMessage()));
    }

    String message =
        e.getAllErrors().stream()
            .map(DefaultMessageSourceResolvable::getDefaultMessage)
            .collect(Collectors.joining());

    return ResponseDTO.fail(
        new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, message));
  }
}
